#include <stdio.h>
#include <pcap.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <time.h>

// 协议类型统计
struct protocol_stats {
    unsigned int total;
    unsigned int ip;
    unsigned int arp;
    unsigned int ipv6;
    unsigned int other;
};

// 将MAC地址转换为可读字符串
void mac_to_string(const u_char *mac, char *buf)
{
    sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

// 打印统计信息
void print_stats(const struct protocol_stats *stats)
{
    printf("\n=== 统计信息 ===\n");
    printf("总帧数: %u\n", stats->total);
    if (stats->total > 0)
	{
        printf("IPv4: %u (%.2f%%)\n", stats->ip, (float)stats->ip / stats->total * 100);
        printf("ARP: %u (%.2f%%)\n", stats->arp, (float)stats->arp / stats->total * 100);
        printf("IPv6: %u (%.2f%%)\n", stats->ipv6, (float)stats->ipv6 / stats->total * 100);
        printf("其他协议: %u (%.2f%%)\n", stats->other, (float)stats->other / stats->total * 100);
    }
}

// 数据包处理回调函数
void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
    struct protocol_stats *stats = (struct protocol_stats *)user_data;
    struct ether_header *eth_header;
    char src_mac[18], dst_mac[18];
    u_short eth_type;
    
    // 获取以太网头部
    eth_header = (struct ether_header *)packet;
    
    // 转换MAC地址为可读格式
    mac_to_string(eth_header->ether_shost, src_mac);
    mac_to_string(eth_header->ether_dhost, dst_mac);
    
    // 获取协议类型
    eth_type = ntohs(eth_header->ether_type);
    char *protocol_str;
    
    // 更新统计信息
    stats->total++;
    switch (eth_type)
	{
        case ETHERTYPE_IP:
            protocol_str = "IPv4";
            stats->ip++;
            break;
        case ETHERTYPE_ARP:
            protocol_str = "ARP";
            stats->arp++;
            break;
        case ETHERTYPE_IPV6:
            protocol_str = "IPv6";
            stats->ipv6++;
            break;
        default:
            protocol_str = "其他";
            stats->other++;
            break;
    }
    
    // 打印捕获的信息
    printf("\n数据包 #%d\n", stats->total);
    printf("捕获时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));
    printf("源MAC: %s\n", src_mac);
    printf("目的MAC: %s\n", dst_mac);
    printf("协议类型: 0x%04x (%s)\n", eth_type, protocol_str);
    printf("数据包大小: %d 字节\n", pkthdr->len);
}

int main(int argc, char *argv[])
{
    pcap_t *handle;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *dev;
    struct bpf_program fp;
    char filter_exp[] = ""; // 可以设置过滤表达式
    bpf_u_int32 net;
    struct protocol_stats stats = {0, 0, 0, 0, 0};
    int count = 0;
    int max_packets = -1; // 默认无限捕获
    
    // 解析命令行参数
    if (argc == 2) max_packets = atoi(argv[1]);
    
    // 查找可用的网络设备
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
	{
        fprintf(stderr, "找不到网络设备: %s\n", errbuf);
        return 1;
    }
    
    printf("使用网络设备: %s\n", dev);
    
    // 打开网络设备进行捕获
    handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL)
	{
        fprintf(stderr, "无法打开设备 %s: %s\n", dev, errbuf);
        return 1;
    }
    
    // 只捕获以太网流量
    if (pcap_datalink(handle) != DLT_EN10MB)
	{
        fprintf(stderr, "设备 %s 不提供以太网头\n", dev);
        pcap_close(handle);
        return 1;
    }
    
    // 编译和设置过滤器
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
	{
        fprintf(stderr, "无法解析过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    if (pcap_setfilter(handle, &fp) == -1)
	{
        fprintf(stderr, "无法安装过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    printf("开始捕获网络数据包...\n");
    printf("按Ctrl+C停止捕获并显示统计信息\n");
    
    // 开始捕获数据包
    if (max_packets > 0) pcap_loop(handle, max_packets, packet_handler, (u_char *)&stats);
    else pcap_loop(handle, -1, packet_handler, (u_char *)&stats);
    
    // 打印统计信息
    print_stats(&stats);
    
    // 清理资源
    pcap_freecode(&fp);
    pcap_close(handle);
    
    return 0;
}